<script setup lang="ts">
import { useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import Vditor from 'vditor'
import { addComment, deleteComment, treeByArticleId } from '@/api/comment'
import { articleDetails, articleLike, articleReadVerifyQuery } from '@/api/article'
import { listTagVo } from '@/api/tags'
import { TagVo } from '@/api/tags/type'
import 'vditor/dist/index.css'
import { ArticleDetailsResp } from '@/api/article/type'
import { UserBasic } from '@/api/type'
import { getConvertDateTime } from '@/utils/dateUtil'
import { CommentAddReq, CommentTreeDto } from '@/api/comment/type'
import useStore from '@/store'
import UserBasicCard from '@/components/user/UserBasicCard.vue'
// store
const store = useStore()
// 文章详情
const articleOne: Ref<ArticleDetailsResp> = ref(<ArticleDetailsResp>{})
// 基础信息
const userBasic = ref(<UserBasic>{})
// 路由
const route = useRoute()
// 标签列表
const tags = ref([] as TagVo[])
// 评论数
const commentCount = ref(<number>0)
// 配置信息
const config = ref({
  dataList: [] as Array<CommentTreeDto>,
  disabled: false,
  articleId: Number.parseInt(route.params.id.toString(), 0)
})

//是否展示阅读遮罩框
const isReadCoverShow = ref(true)

//是否沉浸式阅读
const isImmersiveReading = ref(false)

//打赏Dialog
const rewardDialogVisible = ref(false)

// 是否展示关注按钮
const isShowFollowBtn = ref(false)

// 关注按钮显示文本
const followBtnStr = ref('关注')

// 阅读类型字符串数组
const readTypeStrList = ['无需验证', '点赞', '关注']

/**
 * 点赞处理
 */
async function clickLike(id: number) {
  await articleLike(id).then(({ data: res }) => {
    if (res.code === 200) {
      articleOne.value.isLike = !articleOne.value.isLike
      if (articleOne.value.isLike) {
        // 点赞
        articleOne.value.likeCount++
      } else {
        // 取消点赞
        articleOne.value.likeCount--
      }
    }
  })
}

/**
 * 收藏处理
 * @param id
 */
function clickCollect(id: number) {}

/**
 * 阅读遮罩框是否展示判断
 */
const readCoverShowJudge = (): boolean => {
  const { readVerifyType, isReadVerifyPass } = articleOne.value
  return !!(isReadVerifyPass || readVerifyType == 0)
}

/**
 * 转换阅读类型转换
 */
const readTypeConvert = (readVerifyType: number): string => {
  return readTypeStrList[readVerifyType]
}

/**
 * 跳转到评论
 */
function goComment() {
  let commentElement = document.getElementById('comment')
  if (commentElement) {
    commentElement.scrollIntoView({ behavior: 'smooth' })
  }
}

/**
 * 沉浸式阅读
 */
function immersiveReading() {
  isImmersiveReading.value = !isImmersiveReading.value

  let articleEl: HTMLDivElement | null = document.querySelector('.article')
  if (isImmersiveReading.value) {
    // 修改元素的大小

    if (articleEl) {
      articleEl.style.width = '60%'
    }
  } else {
    if (articleEl) {
      articleEl.style.width = '50%'
    }
  }
}

/**
 * 打开打赏遮罩框
 */
function openRewardDialog() {
  rewardDialogVisible.value = true
}

/**
 * 目录滚动事件
 */
const outlineScrollEvent = () => {
  // 获取所有标题元素
  const previewElement = document.querySelector('.preview')
  // 获取标题元素
  const headingElements = Array.from(previewElement?.children || []).filter(
    (item) => item.tagName.length === 2 && item.tagName !== 'HR' && item.tagName.indexOf('H') === 0
  )
  let toc = []

  // 滚动事件
  window.addEventListener('scroll', () => {
    // 获取大纲项元素
    const outlineItems = document.querySelectorAll('.vditor-outline__item--current')
    const scrollTop = window.scrollY
    // 更新目录项
    toc = headingElements.map((item) => ({
      id: item.id,
      offsetTop: item.offsetTop
    }))

    let highlighted = false

    // 遍历目录项
    for (let i = 0; i < toc.length; i++) {
      if (scrollTop < toc[i].offsetTop - 30) {
        // 清除所有大纲项的当前样式
        outlineItems.forEach((element) => element.classList.remove('vditor-outline__item--current'))
        // 确定当前目录项
        const index = i > 0 ? i - 1 : 0
        const currentElement = document.querySelector(`span[data-target-id="${toc[index].id}"]`)
        if (currentElement) {
          // 添加当前目录项的样式
          currentElement.classList.add('vditor-outline__item--current')
          highlighted = true
        }
        break
      }
    }

    // 如果没有任何部分高于滚动位置，则将最后一个元素设置为当前元素
    if (!highlighted && toc.length > 0) {
      outlineItems.forEach((element) => element.classList.remove('vditor-outline__item--current'))
      const lastElementId = toc[toc.length - 1].id
      const lastElement = document.querySelector(`span[data-target-id="${lastElementId}"]`)
      if (lastElement) {
        // 添加最后一个目录项的样式
        lastElement.classList.add('vditor-outline__item--current')
      }
    }
  })
}

/**
 * markdown处理
 * @param md
 */
async function renderMarkdown(md: string) {
  /** 获取所有的 md渲染标签 */
  // eslint-disable-next-line no-undef
  const previewElement: HTMLDivElement | null = document.querySelector('.preview')
  /** 目录 */
  const outlineElement = document.getElementById('outline') as HTMLDivElement

  if (previewElement) {
    /** markdown 处理 下面的 await 是等待元素渲染完毕，要不然就拿不到对应的元素信息了 */
    await Vditor.preview(previewElement, md, {
      mode: 'light',
      anchor: 2,
      hljs: { style: 'github', lineNumber: true },
      transform(html) {
        return html.replaceAll('<img', '<img referrerPolicy="no-referrer"')
      },
      after() {
        // 事件委托处理图片点击事件
        previewElement.addEventListener('click', (event) => {
          // 使用类型断言告诉 TypeScript 事件的目标是 HTMLImageElement 类型
          const target = event.target as HTMLImageElement
          if (target.tagName === 'IMG') {
            Vditor.previewImage(target)
          }
        })
        if (window.innerWidth <= 768) {
          return
        }
        // 显示大纲并初始化
        Vditor.outlineRender(previewElement, outlineElement)
        if (outlineElement.innerText.trim() !== '') {
          outlineElement.style.display = 'block'
          // 下面这个只是给大纲加一个滚动事件
          outlineScrollEvent()
        }
      }
    })

    // 是否部分展示文章
    if (!readCoverShowJudge()) {
      let previewElRealH = window.getComputedStyle(previewElement).getPropertyValue('height')
      if (previewElRealH) {
        previewElement.style.maxHeight =
          Number.parseInt(previewElRealH.replace('px', '')) / 3 + 'px'
      } else {
        previewElement.style.maxHeight = '1200px'
      }
      previewElement.style.overflow = 'hidden'
    }
  }
}

/**
 * 是否展示关注按钮处理
 */
const showFollowBtnJudgeHandle = () => {
  isShowFollowBtn.value = !(Number.parseInt(store.userId) == articleOne.value.userId)
}

/**
 * 获取文章详情
 */
const getArticle = async () => {
  console.log(route.params)
  await articleDetails(route.params.id).then(({ data: res }) => {
    articleOne.value = res.data
    console.log(articleOne.value)
    userBasic.value = res.data.userBasic
    // 是否展示阅读遮罩框
    isReadCoverShow.value = readCoverShowJudge()
    if (articleOne.value.isFollowed) {
      followBtnStr.value = '已关注'
    } else {
      followBtnStr.value = '关注'
    }
    // markdown处理
    renderMarkdown(articleOne.value.content)
  })
}

/**
 * 标签列表
 */
const getTags = async () => {
  // 函解构用async和await包裹
  const { data: res } = await listTagVo()
  // 对data进行解构赋值 取出请求的结果res
  tags.value = res.data
}

/**
 * 获取评论列表
 */
function getListComment() {
  treeByArticleId(route.params.id).then(({ data: res }) => {
    commentCount.value = res.data.commentCount
    const array = res.data.commentTrees
    for (let i = 0; i < array.length; i++) {
      ;(array[i] as any).replyInputShow = false
    }
    config.value.dataList = array
    config.value.disabled = true
  })
}

/**
 * 提交评论
 * @param val
 */
function submitComments(val: any) {
  const data: CommentAddReq = {
    parentId: val.parentId,
    articleId: val.articleId,
    content: val.content,
    type: 0,
    source: 'COMMENT'
  }
  addComment(data).then(({ data: res }) => {
    if (res.code == 200) {
      getListComment()
      ElMessage.success(res.msg)
    } else {
      ElMessage.error(res.msg)
    }
  })
}

/**
 * 删除评论
 * @param val
 */
function vanishDelete(val: any) {
  deleteComment(val.id).then(({ data: res }) => {
    getListComment()
    ElMessage.success(res.msg)
  })
}

/**
 * 展示全部文章信息
 */
const showEntireArticle = () => {
  isReadCoverShow.value = !isReadCoverShow.value
  // 将元素的最
  const previewElement: HTMLDivElement | null = document.querySelector('.preview')
  if (previewElement) {
    previewElement.style.maxHeight = ''
    previewElement.style.overflow = ''
  }
}

/**
 * 阅读验证查询
 */
const readVerifyQuery = async (): Promise<boolean> => {
  let ans = false
  await articleReadVerifyQuery(articleOne.value.id).then(({ data: res }) => {
    articleOne.value.isReadVerifyPass = res.data
    ans = res.data
  })
  return ans
}

/**
 * 点击阅读遮罩框btn
 */
const coverBtnClick = async () => {
  // 按照类型进行处理
  switch (articleOne.value.readVerifyType) {
    case 0:
    case 2:
      // 直接无需验证了 关注验证暂时没做，先放行
      showEntireArticle()
      break
    case 1:
      // 调用点赞接口
      await clickLike(articleOne.value.id)
      // 校验
      if (await readVerifyQuery()) {
        showEntireArticle()
      }
      break
  }
  isReadCoverShow.value = readCoverShowJudge()
}

/**
 * 组件挂载之前
 */
onBeforeMount(async () => {
  // 监听$route对象上的参数属性变化
  watch(
    () => route.params.id,
    (newId, oldId) => {
      if (newId !== oldId) {
        // 如果发生变化重新载入
        window.location.reload()
      }
    }
  )
  // 获取文章并渲染到页面
  await getArticle()
  // 判断是否展示关注按钮
  showFollowBtnJudgeHandle()
  // 获取标签列表
  await getTags()
})

/**
 * 滚轮事件
 */
const handleScroll = () => {
  let infoEl = document.getElementById('user-info')
  if (infoEl) {
    let top = infoEl.getBoundingClientRect().top
    let height = infoEl.offsetHeight
    let infoElBottomToTop = height + top
    let catalogGroup: HTMLDivElement | null = document.querySelector('.right-catalog-group')
    if (catalogGroup) {
      if (infoElBottomToTop <= 70) {
        // 可以设置
        catalogGroup.style.position = 'fixed'
        catalogGroup.style.top = '90px'
        let realInfoElStyle = window.getComputedStyle(infoEl)
        catalogGroup.style.width = realInfoElStyle.getPropertyValue('width')
      } else {
        catalogGroup.style.position = ''
      }
    }
  }
}

/**
 * 复制时间
 */
const copyEvent = (e: ClipboardEvent) => {
  let clipboardData = e.clipboardData
  if (!clipboardData) return
  let selection: Selection | null = window.getSelection()
  if (selection) {
    let text = selection.toString()

    if (text) {
      // 如果文本存在，首先取消默认行为
      e.preventDefault()
      // 通过调用 clipboardData 对象的 setData(format,data) 方法，设置相关文本
      // format 一个 DOMString 类型 表示要添加到 drag object 的拖动数据的类型
      // data 一个 DOMString 表示要添加到 drag object 的数据
      text = text.trim()
      if (text.length > 50) {
        // 超过50个字才加信息
        // TODO 后面要加上链接
        let cppyAddStr =
          '————————————————————————————————————————————————————————————————————————————————\n' +
          '版权声明：本文为博主原创文章，遵循 CC 4.0 BY-SA 版权协议，转载请附上原文出处链接和本声明。\n' +
          '原文链接：TODO 后面再改'
        text += '\n' + cppyAddStr
      }
      // 默认复制追加信息：

      clipboardData.setData('text/plain', text)
      ElMessage.success('复制成功')
    }
  }
}

/**
 * 是否展示标签
 * @param tags
 * @param id
 */
const isTagShow = (tags: number[], id: number) => {
  return tags && tags.includes(id)
}

/** 组件挂载时 */
onMounted(() => {
  // 使用 nextTick 来获取元素宽度，确保在 DOM 更新之后
  // nextTick(() => {})
  // 添加复制事件
  document.addEventListener('copy', copyEvent)
  // 监听滚动事件
  window.addEventListener('scroll', handleScroll)
  // window.addEventListener('scroll', handlePreviewScroll);
  // 获取评论列表
  getListComment()
})

/**
 * 组件销毁时
 */
onUnmounted(() => {
  // 删除复制事件
  document.removeEventListener('copy', copyEvent)
  // 在组件销毁时移除滚动事件监听器
  window.removeEventListener('scroll', handleScroll)
  // window.removeEventListener('scroll', handlePreviewScroll);
})
</script>

<template>
  <div class="article-container">
    <!-- 电脑端 -->
    <!--左边点赞栏-->
    <div class="left-tip-group">
      <el-tooltip effect="dark" content="点赞" placement="left">
        <div class="left-tip-item" title="点赞" @click="clickLike(articleOne.id)">
          <el-badge
            class="tip-badge"
            :value="articleOne.likeCount"
            :max="99"
            :hidden="articleOne.likeCount < 1"
          >
            <svg-icon v-if="articleOne.isLike" icon-class="like-yes" />
            <svg-icon v-else icon-class="like-no" />
          </el-badge>
        </div>
      </el-tooltip>
      <el-tooltip effect="dark" content="收藏" placement="left">
        <div class="left-tip-item" title="收藏" @click="clickCollect(articleOne.id)">
          <el-badge
            class="tip-badge"
            :value="articleOne.collectCount"
            :hidden="articleOne.collectCount < 1"
          >
            <svg-icon v-if="articleOne.isCollect" icon-class="collect-yes" />
            <svg-icon v-else icon-class="collect-no" />
          </el-badge>
        </div>
      </el-tooltip>
      <el-tooltip effect="dark" content="评论" placement="left">
        <div class="left-tip-item" title="评论" @click="goComment">
          <el-badge class="tip-badge" :value="articleOne.commentCount">
            <svg-icon icon-class="comment" />
          </el-badge>
        </div>
      </el-tooltip>
      <el-tooltip effect="dark" content="沉浸式阅读" placement="left">
        <div class="left-tip-item" title="沉浸式阅读" @click="immersiveReading">
          <el-badge class="tip-badge">
            <svg-icon icon-class="full-screen" />
          </el-badge>
        </div>
      </el-tooltip>
      <el-tooltip effect="dark" content="打赏" placement="left">
        <div class="left-tip-item" title="打赏" @click="openRewardDialog">
          <el-badge class="tip-badge">
            <svg-icon icon-class="reward" />
          </el-badge>
        </div>
      </el-tooltip>
    </div>
    <!--文章区域-->
    <el-card class="box-card article" shadow="hover">
      <!--标题-->
      <h1 class="flex-center">{{ articleOne.title }}</h1>
      <!--文章特征信息-->
      <span class="article-feature">
        <!--作者，分类，标签-->
        <span class="article-l">
          <el-space size="default">
            <el-avatar v-if="userBasic.avatar" size="default" :src="userBasic.avatar" />
            <span class="text-sm">{{ userBasic.username }}</span>
            <button v-show="isShowFollowBtn" class="follow-btn">{{ followBtnStr }}</button>
            <el-tag size="default">{{ articleOne.categoryName }}</el-tag>
            <template v-for="(item, index) in tags">
              <el-tag
                v-if="isTagShow(articleOne.tags, item.id)"
                :key="index"
                style="margin-right: 4px"
                type="success"
                size="default"
                :label="index"
                border
                >{{ item.name }}
              </el-tag>
            </template>
          </el-space>
        </span>
        <!--文章点赞、评论、阅读-->
        <el-space class="article-r" alignment="center" size="large">
          <!--创建时间-->
          <span class="text-sm font-number text-color">
            <svg-icon icon-class="time-light" />
            {{ getConvertDateTime(articleOne.createTime) }}
          </span>
          <!--阅读数量-->
          <span class="text-sm font-number text-color">
            <svg-icon icon-class="eye-light" style="font-size: 15px" />
            {{ articleOne.viewCount }}
          </span>
          <!--评论数-->
          <span class="text-sm font-number text-color">
            <svg-icon icon-class="message" style="font-size: 15px" />
            {{ commentCount }}
          </span>
          <!--点赞-->
          <span class="text-sm font-number" @click="clickLike(articleOne.id)">
            <svg-icon
              :icon-class="articleOne.isLike ? 'give-dark' : 'give-light'"
              style="font-size: 15px; cursor: pointer"
              :style="{ color: articleOne.isLike ? '#fd5a5a' : '' }"
            />
            {{ articleOne.likeCount }}
          </span>
        </el-space>
      </span>
      <hr class="divider" />
      <!--是否原创提示-->
      <div>
        <div v-if="articleOne.isOriginal == 1" class="tip">
          原创 本文C-Blog原创文章，转载无需和我联系，但请注明来自本站<br />
        </div>
        <div v-else class="tip">
          转载 本文转载自
          <a :href="articleOne.reproductionUrl" target="_blank">{{ articleOne.reproductionUrl }}</a>
          <br />
        </div>
      </div>
      <!--文章-->
      <div class="preview"></div>
      <!--文章遮罩框-->
      <div v-show="!isReadCoverShow">
        <div class="read-cover">
          <div class="read-cover-head">该文章部分内容已隐藏</div>
          <div class="read-cover-content">
            <div class="cover-content-type">扫码阅读</div>
            <div>
              文章剩余部分已隐藏，请 {{ readTypeConvert(articleOne.readVerifyType) }} 后进行查看
            </div>
            <el-button class="cover-btn" type="primary" @click="coverBtnClick">验证</el-button>
          </div>
        </div>
      </div>
      <hr class="divider" />
      <Comment
        v-if="config.disabled"
        id="comment"
        :data-list="config.dataList"
        :article-id="config.articleId"
        :source="'COMMENT'"
        @list-comments="getListComment"
        @submit-comments="submitComments"
        @delete-comments="vanishDelete"
      ></Comment>
    </el-card>
    <!--右内容区-->
    <div class="right-info-group">
      <div v-show="!isImmersiveReading">
        <!--用户基本信息显示卡-->
        <UserBasicCard id="user-info" :user-basic="userBasic" />
        <el-card class="box-card right-catalog-group" shadow="hover">
          <template #header>
            <h2 class="text-lg" style="margin: 0">
              <svg-icon icon-class="tags"></svg-icon>
              目录
            </h2>
          </template>
          <!--目录-->
          <div class="catalog">
            <div id="outline"></div>
          </div>
        </el-card>
      </div>
    </div>
    <!--dialog区域-->
    <el-dialog v-model="rewardDialogVisible" title="Tips" width="500">
      <span>This is a message</span>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="rewardDialogVisible = false">Cancel</el-button>
          <el-button type="primary" @click="rewardDialogVisible = false"> Confirm</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<style scoped>
.box-card {
  padding: 0;
  border-radius: 10px;
  border: 1px solid transparent;
}

.tip {
  padding: 10px;
  margin: 20px auto 15px;
  background-color: #ecf8ff;
  border-radius: 4px;
  border-left: 5px solid #50bfff;
  color: #888;
}

/*样式穿透 md文件*/
>>> .github-markdown-body {
  padding: 0;
}
</style>
<style lang="scss" scoped>
.vditor-outline__item--current {
  border-left: 2px solid #fd5a5a;
  color: #fd5a5a !important;
  background-color: #f6f8fa;
}

.vditor-outline li > span:hover {
  color: #fd5a5a !important;
  background-color: #f6f8fa;
}

.vditor-outline {
  width: 100%;
}

.vditor-reset {
  color: var(--el-text-color-primary) !important;
}

@media screen and (max-width: 1199px) {
  :deep(.main-container) {
    padding: 0 !important;
  }
  .article-container {
    width: 100%;
    :deep(.left-tip-group) {
      display: none !important;
    }

    .article {
      width: 100%;
    }

    :deep(.right-info-group) {
      display: none !important;
    }
  }
}

@media screen and (min-width: 1200px) {
  .article-container {
    display: flex;
    justify-content: center;

    .left-tip-group {
      position: fixed;
      top: 150px;
      left: 152px;

      .left-tip-item {
        display: flex;
        margin-bottom: 20px;
        width: 50px;
        height: 50px;
        background-color: var(--background-color);
        border-radius: 50%;
        text-align: center;

        &:hover {
          color: #409eff;
          background-color: rgba(25, 153, 153, 0.2);
        }

        .tip-badge {
          display: flex;
          position: relative;
          width: 100%;
          align-items: center;
          line-height: 16px;
          justify-items: center;
          justify-content: center;

          svg {
            font-size: 20px;
          }
        }
      }

      .reward {
        position: absolute;
        width: 50px;
        height: 50px;

        .reward-cover {
          display: none;
          position: relative;
          left: 52px;
          bottom: -150px;
          width: 200px;
          height: 200px;
          background: rgba(89, 86, 86, 0.243);
          border-radius: 5px;

          &::before {
            content: '';
            float: left;
            position: relative;
            top: calc(100px - 1.5em);
            left: -40px;
            display: block;
            height: 0;
            width: 0;
            border-top: 20px solid transparent;
            border-bottom: 20px solid transparent;
            border-left: 20px solid transparent;
            border-right: 20px solid rgba(89, 86, 86, 0.243);
          }
        }

        &:hover .reward-cover {
          display: block;
        }
      }
    }

    .article {
      width: 50%;
      .article-feature {
        display: flex;
        align-items: center;
        justify-content: space-between;
        .article-l {
          display: flex;
          align-items: center;
        }
      }
      .read-cover {
        position: relative;
        background: var(--article-srect-background);
        height: 200px;
        margin-top: 10px;
        padding: 10px;

        &::before {
          content: '';
          position: absolute;
          top: -80px;
          left: 0;
          width: 100%;
          height: 80px;
          z-index: 2;
          background: var(--read-cover-before-bgc);
        }

        .read-cover-head {
        }

        .read-cover-content {
          margin: 10px;
          height: 150px;
          text-align: center;
          background: var(--background-color);
          border-radius: 5px;

          .cover-content-type {
            position: relative;
            top: 0;
            left: 0;
            width: 80px;
            height: 24px;
            text-align: center;
            border-top-left-radius: 5px;
            border-bottom-right-radius: 5px;
            background: var(--read-type-bgc);
          }

          .cover-btn {
            margin-top: 20px;
          }
        }
      }
    }

    .right-info-group {
      margin-left: 20px;
      width: 20%;

      .right-catalog-group {
        margin-top: 20px;

        .catalog {
          overflow: hidden;
          max-height: calc(100vh - 410px);
        }

        /* 在滑动时显示滚动条 */

        .catalog:hover {
          overflow-y: auto;
        }
      }
    }
  }
}
</style>
